/***************************************************************************
 *   Copyright (C) 2009 - 2010 by Simon Qian <SimonQian@SimonQian.com>     *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "vsf.h"
#include "usrapp.h"

struct app_hwcfg_t
{
	struct
	{
		struct vsfhal_gpio_pin_t pullup;
	} usbd;
} static const app_hwcfg =
{
	.usbd.pullup.port = USB_PULLUP_PORT,
	.usbd.pullup.pin = USB_PULLUP_PIN,
};

struct usrapp_param_t
{
	struct
	{
		uint8_t DeviceDescriptor[18];
		uint8_t ConfigDescriptor[75];
		uint8_t StringLangID[4];
		uint8_t StringVendor[20];
		uint8_t StringProduct[14];
		uint8_t StringFunc_RNDIS[18];
		struct vsfusbd_desc_filter_t StdDesc[7];
	} usbd;
} static const usrapp_param =
{
	.usbd.DeviceDescriptor =
	{
		USB_DT_DEVICE_SIZE,
		USB_DT_DEVICE,
		0x00, 0x02,	// bcdUSB
		0xEF,		// device class: IAD
		0x02,		// device sub class
		0x01,		// device protocol
		64,			// max packet size
		(GENERATE_HEX(APPCFG_USBD_VID) >> 0) & 0xFF,
		(GENERATE_HEX(APPCFG_USBD_VID) >> 8) & 0xFF,
					// vendor
		(GENERATE_HEX(APPCFG_USBD_PID) >> 0) & 0xFF,
		(GENERATE_HEX(APPCFG_USBD_PID) >> 8) & 0xFF,
					// product
		0x00, 0x02,	// bcdDevice
		1,			// manu facturer
		2,			// product
		0,			// serial number
		1,			// number of configuration
	},
	.usbd.ConfigDescriptor =
	{
		USB_DT_CONFIG_SIZE,
		USB_DT_CONFIG,
		(sizeof(usrapp_param.usbd.ConfigDescriptor) >> 0) & 0xFF,
		(sizeof(usrapp_param.usbd.ConfigDescriptor) >> 8) & 0xFF,
					// wTotalLength
		0x02,		// bNumInterfaces: 2 interfaces
		0x01,		// bConfigurationValue: Configuration value
		0x00,		// iConfiguration: Index of string descriptor describing the configuration
		0x80,		// bmAttributes: bus powered
		0x64,		// MaxPower

		// IDA for RNDIS
		USB_DT_INTERFACE_ASSOCIATION_SIZE,
		USB_DT_INTERFACE_ASSOCIATION,
		0,			// bFirstInterface
		2,			// bInterfaceCount
		0x02,		// bFunctionClass
		0x06,		// bFunctionSubClass
		0x00,		// bFunctionProtocol
		0x04,		// iFunction

		USB_DT_INTERFACE_SIZE,
		USB_DT_INTERFACE,
		0x00,		// bInterfaceNumber: Number of Interface
		0x00,		// bAlternateSetting: Alternate setting
		0x01,		// bNumEndpoints
		0x02,		// bInterfaceClass:
		0x02,		// bInterfaceSubClass:
		0xFF,		// nInterfaceProtocol:
		0x04,		// iInterface:

		// Header Functional Descriptor
		0x05,		// bLength: Endpoint Descriptor size
		0x24,		// bDescriptorType: CS_INTERFACE
		0x00,		// bDescriptorSubtype: Header Func Desc
		0x10,		// bcdCDC: spec release number
		0x01,

		// Call Managment Functional Descriptor
		0x05,		// bFunctionLength
		0x24,		// bDescriptorType: CS_INTERFACE
		0x01,		// bDescriptorSubtype: Call Management Func Desc
		0x00,		// bmCapabilities: D0+D1
		0x01,		// bDataInterface: 1

		// ACM Functional Descriptor
		0x04,		// bFunctionLength
		0x24,		// bDescriptorType: CS_INTERFACE
		0x02,		// bDescriptorSubtype: Abstract Control Management desc
		0x00,		// bmCapabilities

		// Union Functional Descriptor
		0x05,		// bFunctionLength
		0x24,		// bDescriptorType: CS_INTERFACE
		0x06,		// bDescriptorSubtype: Union func desc
		0,			// bMasterInterface: Communication class interface
		1,			// bSlaveInterface0: Data Class Interface

		USB_DT_ENDPOINT_SIZE,
		USB_DT_ENDPOINT,
		0x81,		// bEndpointAddress: (IN1)
		0x03,		// bmAttributes: Interrupt
		8, 0x00,	// wMaxPacketSize:
#if defined(VSFUSBD_CFG_HIGHSPEED)
		0x10,		// bInterval:
#elif defined(VSFUSBD_CFG_FULLSPEED)
		0xFF,		// bInterval:
#endif

		USB_DT_INTERFACE_SIZE,
		USB_DT_INTERFACE,
		0x01,		// bInterfaceNumber: Number of Interface
		0x00,		// bAlternateSetting: Alternate setting
		0x02,		// bNumEndpoints
		0x0A,		// bInterfaceClass
		0x00,		// bInterfaceSubClass
		0x00,		// nInterfaceProtocol
		0x00,		// iInterface:

		USB_DT_ENDPOINT_SIZE,
		USB_DT_ENDPOINT,
		0x82,		// bEndpointAddress: (IN2)
		0x02,		// bmAttributes: Bulk
#if defined(VSFUSBD_CFG_HIGHSPEED)
		0x00, 0x02,	// wMaxPacketSize:
#elif defined(VSFUSBD_CFG_FULLSPEED)
		0x40, 0x00,	// wMaxPacketSize:
#endif
		0x00,		// bInterval:

		USB_DT_ENDPOINT_SIZE,
		USB_DT_ENDPOINT,
		0x02,		// bEndpointAddress: (OUT2)
		0x02,		// bmAttributes: Bulk
#if defined(VSFUSBD_CFG_HIGHSPEED)
		0x00, 0x02,	// wMaxPacketSize:
#elif defined(VSFUSBD_CFG_FULLSPEED)
		0x40, 0x00,	// wMaxPacketSize:
#endif
		0x00,		// bInterval:
	},
	.usbd.StringLangID =
	{
		4,
		USB_DT_STRING,
		0x09,
		0x04,
	},
	.usbd.StringVendor =
	{
		20,
		USB_DT_STRING,
		'S', 0, 'i', 0, 'm', 0, 'o', 0, 'n', 0, 'Q', 0, 'i', 0, 'a', 0,
		'n', 0,
	},
	.usbd.StringProduct =
	{
		14,
		USB_DT_STRING,
		'V', 0, 'S', 0, 'F', 0, 'U', 0, 'S', 0, 'B', 0,
	},
	.usbd.StringFunc_RNDIS =
	{
		18,
		USB_DT_STRING,
		'V', 0, 'S', 0, 'F', 0, 'R', 0, 'N', 0, 'D', 0, 'I', 0, 'S', 0
	},
	.usbd.StdDesc =
	{
		VSFUSBD_DESC_DEVICE(0, usrapp_param.usbd.DeviceDescriptor, sizeof(usrapp_param.usbd.DeviceDescriptor)),
		VSFUSBD_DESC_CONFIG(0, 0, usrapp_param.usbd.ConfigDescriptor, sizeof(usrapp_param.usbd.ConfigDescriptor)),
		VSFUSBD_DESC_STRING(0, 0, usrapp_param.usbd.StringLangID, sizeof(usrapp_param.usbd.StringLangID)),
		VSFUSBD_DESC_STRING(0x0409, 1, usrapp_param.usbd.StringVendor, sizeof(usrapp_param.usbd.StringVendor)),
		VSFUSBD_DESC_STRING(0x0409, 2, usrapp_param.usbd.StringProduct, sizeof(usrapp_param.usbd.StringProduct)),
		VSFUSBD_DESC_STRING(0x0409, 4, usrapp_param.usbd.StringFunc_RNDIS, sizeof(usrapp_param.usbd.StringFunc_RNDIS)),
		VSFUSBD_DESC_NULL,
	},
};
const char index_html[] =
"<!DOCTYPE html><html>"
"<title>main</title>"
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">"
"<meta name=\"generator\" content=\"Google Web Designer 1.0.1.1025\">"
"<meta name=\"viewport\" content=\"width=device-width\"/>"
"</head>"
"<p>VSFIP HTTP DEMO</p>"
"<form action=\"submit\" method=\"post\" enctype=\"application/x-www-form-urlencoded\" name=\"form\" target=\"_self\">"
"<p><input name=\"Text\" type=\"text\" value=\"TEXT\" maxlength=\"16\"></p>"
"<p><input type=\"checkbox\" name=\"checkbox\" id=\"checkbox\">CheckBox</p>"
"<p><input name=\"radio\" type=\"radio\" id=\"radio\" value=\"ON\" checked>CheckButton LEDON</p>"
"<p><input type=\"radio\" name=\"radio\" id=\"radio2\" value=\"OFF\">CheckButton LEDOFF</p>"
"<p><select name=\"select\" id=\"select\">"
"<option value=\"1\">1</option>"
"<option value=\"2\">2</option>"
"<option value=\"3\">3</option>"
"<option value=\"4\">4</option>"
"</select>Menu</p>"
"<p><input type=\"submit\" name=\"button\" id=\"button\" value=\"Submit\">Submit</p>"
"</form></body></html>";

static vsf_err_t usrapp_net_app(struct vsfsm_pt_t *pt, vsfsm_evt_t evt)
{
	struct usrapp_net_t *net = (struct usrapp_net_t *)pt->user_data;
	struct vsfsm_pt_t *caller_pt = &net->caller_pt;
	vsf_err_t err;

	vsfsm_pt_begin(pt);

	caller_pt->sm = pt->sm;
	err = vsfip_dnsc_setserver(0, &net->netif.dns[0]);
	if (err) return err;

	while (1)
	{
		caller_pt->state = 0;
		vsfsm_pt_entry(pt);
		err = vsfip_gethostbyname(caller_pt, evt, "www.baidu.com", &net->dns_ip);
		if (err > 0) return err; else if (err < 0)
		{
			vsfdbg_printf("fail to get host for www.baidu.com" VSFCFG_DEBUG_LINEEND);
			return err;
		}

		vsfdbg_printf("baidu.com: %d.%d.%d.%d" VSFCFG_DEBUG_LINEEND,
				net->dns_ip.addr.s_addr_buf[0], net->dns_ip.addr.s_addr_buf[1],
				net->dns_ip.addr.s_addr_buf[2], net->dns_ip.addr.s_addr_buf[3]);

		vsfsm_pt_delay(pt, 5000);
	}

	vsfsm_pt_end(pt);
	return VSFERR_NONE;
}

static void usrapp_net_on_dhcpc_notify(void *param)
{
	struct usrapp_net_t *net = (struct usrapp_net_t *)param;
	struct vsfip_netif_t *netif = &net->netif;
	struct vsfip_dhcpc_t *dhcpc = &net->dhcpc;

	if (dhcpc->ready)
	{
		vsfdbg_printf("dhcpc:" VSFCFG_DEBUG_LINEEND
			"\tipaddr: %d.%d.%d.%d" VSFCFG_DEBUG_LINEEND
			"\tnetmask: %d.%d.%d.%d" VSFCFG_DEBUG_LINEEND
			"\tgameway: %d.%d.%d.%d" VSFCFG_DEBUG_LINEEND
			"\tdns: %d.%d.%d.%d" VSFCFG_DEBUG_LINEEND,
			netif->ipaddr.addr.s_addr_buf[0], netif->ipaddr.addr.s_addr_buf[1],
			netif->ipaddr.addr.s_addr_buf[2], netif->ipaddr.addr.s_addr_buf[3],
			netif->netmask.addr.s_addr_buf[0], netif->netmask.addr.s_addr_buf[1],
			netif->netmask.addr.s_addr_buf[2], netif->netmask.addr.s_addr_buf[3],
			netif->gateway.addr.s_addr_buf[0], netif->gateway.addr.s_addr_buf[1],
			netif->gateway.addr.s_addr_buf[2], netif->gateway.addr.s_addr_buf[3],
			netif->dns[0].addr.s_addr_buf[0], netif->dns[0].addr.s_addr_buf[1],
			netif->dns[0].addr.s_addr_buf[2], netif->dns[0].addr.s_addr_buf[3]);

		net->ipaddr = dhcpc->ipaddr;
		net->pt.user_data = net;
		net->pt.thread = usrapp_net_app;
		vsfsm_pt_init(&net->sm, &net->pt);
	}
	else
	{
		dhcpc->ipaddr.size = 0;
		vsfip_dhcpc_start(netif, dhcpc);
	}
}

static void usrapp_net_on_connect(void *param, struct vsfip_netdrv_t *netdrv)
{
	struct usrapp_net_t *net = (struct usrapp_net_t *)param;
	struct vsfip_netif_t *netif = &net->netif;

	if (!netif->netdrv)
		vsfip_netif_set_netdrv(netif, netdrv);
}

static void usrapp_net_on_connected(void *param, struct vsfip_netdrv_t *netdrv)
{
	struct usrapp_net_t *net = (struct usrapp_net_t *)param;
	struct vsfip_netif_t *netif = &net->netif;

	vsfdbg_printf("%s_mac: %02X:%02X:%02X:%02X:%02X:%02X" VSFCFG_DEBUG_LINEEND,
		net->name,
		netdrv->macaddr.addr.s_addr_buf[0], netdrv->macaddr.addr.s_addr_buf[1],
		netdrv->macaddr.addr.s_addr_buf[2], netdrv->macaddr.addr.s_addr_buf[3],
		netdrv->macaddr.addr.s_addr_buf[4], netdrv->macaddr.addr.s_addr_buf[5]);

	if (net->host)
		vsfip_dhcpd_start(netif, &net->dhcpd);
	else
	{
		if (net->ipaddr.size)
			net->dhcpc.ipaddr = net->ipaddr;
		vsfsm_notifier_set_cb(&net->dhcpc.notifier, usrapp_net_on_dhcpc_notify, net);
		vsfip_dhcpc_start(netif, &net->dhcpc);
	}
}

static void usrapp_net_on_disconnect(void *param, struct vsfip_netdrv_t *netdrv)
{
	struct usrapp_net_t *net = (struct usrapp_net_t *)param;
	if (net->host)
		vsfip_dhcpd_stop(&net->dhcpd);
	else
	{
		vsfip_dhcpc_stop(&net->dhcpc);
		vsfip_cancel_gethostbyname();
		vsfsm_fini(&net->sm);
	}
}

static void usrapp_net_on_data(void *param, uint8_t *buf, uint32_t size, bool out)
{
	struct usrapp_net_t *net = (struct usrapp_net_t *)param;
	vsfdbg_printf("%s_%s :" VSFCFG_DEBUG_LINEEND, net->name, out ? "output" : "input");
	vsfdbg_printb(buf, size, VSFDBG_DF_DEFAULT);
}

static vsf_err_t usrapp_tcpip_init(struct vsfsm_pt_t *pt, vsfsm_evt_t evt);
struct usrapp_t usrapp =
{
	.debug.stream_tx.stream.op				= &vsf_fifostream_op,
	.debug.stream_tx.mem.buffer.buffer		= (uint8_t *)&usrapp.debug.txbuff,
	.debug.stream_tx.mem.buffer.size		= sizeof(usrapp.debug.txbuff),
	.debug.stream_rx.stream.op				= &vsf_fifostream_op,
	.debug.stream_rx.mem.buffer.buffer		= (uint8_t *)&usrapp.debug.rxbuff,
	.debug.stream_rx.mem.buffer.size		= sizeof(usrapp.debug.rxbuff),

	.debug.uart_stream.index				= DEBUG_UART_INDEX,
	.debug.uart_stream.mode					= VSFHAL_USART_STOPBITS_1 | VSFHAL_USART_PARITY_NONE,
	.debug.uart_stream.int_priority			= 0xFF,
	.debug.uart_stream.baudrate				= 921600,
	.debug.uart_stream.stream_tx			= &usrapp.debug.stream_tx.stream,
	.debug.uart_stream.stream_rx			= &usrapp.debug.stream_rx.stream,

	.usbh.hcd_param.index					= VSFHAL_HCD_INDEX,
	.usbh.hcd_param.int_priority			= 0xFF,
	.usbh.usbh.hcd							= &vsfohci_drv,
	.usbh.usbh.hcd.param					= &usrapp.usbh.hcd_param,

	.fakefat32.root_dir =
	{
		// "/"
		{
			.memfile.file.name = "VSFMSC",
			.memfile.file.attr = VSFILE_ATTR_VOLUMID,
		},
		{
			.memfile.file.name = "webroot",
			.memfile.file.attr = VSFILE_ATTR_DIRECTORY,
			.memfile.d.child = (struct vsfile_memfile_t *)&usrapp.fakefat32.root_dir[3],
		},
		{
			.memfile.file.name = NULL,
		},
		// "/webroot"
		{
			.memfile.file.name = ".",
			.memfile.file.attr = VSFILE_ATTR_DIRECTORY,
		},
		{
			.memfile.file.name = "..",
			.memfile.file.attr = VSFILE_ATTR_DIRECTORY,
		},
		{
			.memfile.file.name = "index.html",
			.memfile.file.size = sizeof(index_html) - 1,
			.memfile.file.attr = VSFILE_ATTR_ARCHIVE | VSFILE_ATTR_READONLY,
			.memfile.f.buff = (uint8_t *)index_html,
		},
		{
			.memfile.file.name = NULL,
		},
	},
	.fakefat32.param.sector_size					= 512,
	.fakefat32.param.sector_number					= 0x00001000,
	.fakefat32.param.sectors_per_cluster			= 8,
	.fakefat32.param.volume_id						= 0x0CA93E47,
	.fakefat32.param.disk_id						= 0x12345678,
	.fakefat32.param.root[0].memfile.file.name		= "ROOT",
	.fakefat32.param.root[0].memfile.d.child		= (struct vsfile_memfile_t *)usrapp.fakefat32.root_dir,

	.usbd.rndis.param.CDCACM.CDC.ep_notify			= 1,
	.usbd.rndis.param.CDCACM.CDC.ep_out				= 2,
	.usbd.rndis.param.CDCACM.CDC.ep_in				= 2,
	.usbd.rndis.param.mac.size						= 6,
	.usbd.rndis.param.mac.addr.s_addr64				= 0x0605040302E0,
	.usbd.rndis.param.netdrv.cb						= &usrapp.usbd.rndis.param.cb,
	.usbd.rndis.param.netdrv.macaddr.size			= 6,
	.usbd.rndis.param.netdrv.macaddr.addr.s_addr64	= 0x0E0D0C0B0AE0,
	.usbd.rndis.param.cb.param						= &usrapp.net.usbd_rndis,
	.usbd.rndis.param.cb.on_connect					= usrapp_net_on_connect,
	.usbd.rndis.param.cb.on_connected				= usrapp_net_on_connected,
	.usbd.rndis.param.cb.on_disconnect				= usrapp_net_on_disconnect,
	.usbd.rndis.param.cb.on_data					= usrapp_net_on_data,

	.usbd.ifaces[0].class_protocol			= (struct vsfusbd_class_protocol_t *)&vsfusbd_RNDISControl_class,
	.usbd.ifaces[0].protocol_param			= &usrapp.usbd.rndis.param,
	.usbd.ifaces[1].class_protocol			= (struct vsfusbd_class_protocol_t *)&vsfusbd_RNDISData_class,
	.usbd.ifaces[1].protocol_param			= &usrapp.usbd.rndis.param,
	.usbd.config[0].num_of_ifaces			= dimof(usrapp.usbd.ifaces),
	.usbd.config[0].iface					= usrapp.usbd.ifaces,
	.usbd.device.num_of_configuration		= dimof(usrapp.usbd.config),
	.usbd.device.config						= usrapp.usbd.config,
	.usbd.device.desc_filter				= (struct vsfusbd_desc_filter_t *)usrapp_param.usbd.StdDesc,
	.usbd.device.device_class_iface			= 0,
	.usbd.device.drv						= (struct vsfhal_usbd_t *)&vsfhal_usbd,
	.usbd.device.int_priority				= 0xFF,

	.tcpip.httpd		=
	{
		.service_num	= dimof(usrapp.tcpip.httpd_services),
		.service		= usrapp.tcpip.httpd_services,
		.homepage		= "/index.html",
	},
	.tcpip.pt			=
	{
		.user_data		= &usrapp,
		.thread			= usrapp_tcpip_init,
	},

	.net.usbh_ipheth	=
	{
		.name			= "usbh_ipheth",
		.host			= false,
		.netif.op		= &vsfip_eth_op,
	},
	.net.usbh_ecm		=
	{
		.name			= "usbh_ecm",
		.host			= false,
		.netif.op		= &vsfip_eth_op,
	},
	.net.usbh_rndis		=
	{
		.name			= "usbh_rndis",
		.host			= false,
		.netif.op		= &vsfip_eth_op,
	},
	.net.usbd_rndis		=
	{
		.name						= "usbd_rndis",
		.host						= true,
		.netif.op					= &vsfip_eth_op,
		.netif.ipaddr.size			= 4,
		.netif.ipaddr.addr.s_addr	= 0x01202020,
		.netif.netmask.size			= 4,
		.netif.netmask.addr.s_addr	= 0x00FFFFFF,
		.netif.gateway.size			= 4,
		.netif.gateway.addr.s_addr	= 0x01202020,
	},
};

// vsfip buffer manager
static struct vsfip_buffer_t* usrapp_vsfip_get_buffer(uint32_t size)
{
	struct vsfip_buffer_t *buffer = vsf_bufmgr_malloc(sizeof(*buffer) + size);
	if (buffer != NULL)
		buffer->buffer = (uint8_t *)&buffer[1];
	return buffer;
}

static void usrapp_vsfip_release_buffer(struct vsfip_buffer_t *buffer)
{
	vsf_bufmgr_free(buffer);
}

static struct vsfip_socket_t* usrapp_vsfip_get_socket(void)
{
	return (struct vsfip_socket_t *)vsf_bufmgr_malloc(sizeof(struct vsfip_socket_t));
}

static void usrapp_vsfip_release_socket(struct vsfip_socket_t *socket)
{
	vsf_bufmgr_free(socket);
}

static struct vsfip_tcppcb_t* usrapp_vsfip_get_tcppcb(void)
{
	return (struct vsfip_tcppcb_t *)vsf_bufmgr_malloc(sizeof(struct vsfip_tcppcb_t));
}

static void usrapp_vsfip_release_tcppcb(struct vsfip_tcppcb_t *tcppcb)
{
	vsf_bufmgr_free(tcppcb);
}

const struct vsfip_mem_op_t usrapp_vsfip_mem_op =
{
	.get_buffer		= usrapp_vsfip_get_buffer,
	.release_buffer	= usrapp_vsfip_release_buffer,
	.get_socket		= usrapp_vsfip_get_socket,
	.release_socket	= usrapp_vsfip_release_socket,
	.get_tcppcb		= usrapp_vsfip_get_tcppcb,
	.release_tcppcb	= usrapp_vsfip_release_tcppcb,
};

// vsfile_memop
struct vsfile_vfsfile_t* usrapp_vsfile_alloc_vfs(void)
{
	return VSFPOOL_ALLOC(&usrapp.fs.vfsfile_pool, struct vsfile_vfsfile_t);
}

static void usrapp_vsfile_free_vfs(struct vsfile_vfsfile_t *vfsfile)
{
	VSFPOOL_FREE(&usrapp.fs.vfsfile_pool, vfsfile);
}

static const struct vsfile_memop_t usrapp_vsfile_memop =
{
	.alloc_vfs = usrapp_vsfile_alloc_vfs,
	.free_vfs = usrapp_vsfile_free_vfs,
};

static vsf_err_t usrapp_tcpip_init(struct vsfsm_pt_t *pt, vsfsm_evt_t evt)
{
	struct usrapp_t *app = (struct usrapp_t *)pt->user_data;
	struct vsfsm_pt_t *caller_pt = &app->tcpip.caller_pt;
	struct vsfip_httpd_t *httpd = &app->tcpip.httpd;
	vsf_err_t err;
	vsfsm_pt_begin(pt);

	caller_pt->sm = pt->sm;
	VSFPOOL_INIT(&app->fs.vfsfile_pool, struct vsfile_vfsfile_t, APPCFG_VSFILE_NUM);
	vsfile_init((struct vsfile_memop_t *)&usrapp_vsfile_memop);

	caller_pt->state = 0;
	vsfsm_pt_entry(pt);
	err = vsfile_getfile(caller_pt, evt, NULL, "/", &app->fs.file);
	if (err) return err;

	caller_pt->state = 0;
	caller_pt->user_data = &app->fakefat32.param;
	vsfsm_pt_entry(pt);
	err = vsfile_mount(caller_pt, evt, (struct vsfile_fsop_t *)&fakefat32_fs_op, app->fs.file);
	if (err) return err;

	vsfip_init((struct vsfip_mem_op_t *)&usrapp_vsfip_mem_op);
	vsfip_dnsc_init();

	caller_pt->state = 0;
	vsfsm_pt_entry(pt);
	err = vsfile_getfile(caller_pt, evt, NULL, APPCFG_HTTPD_ROOT, &httpd->root);
	if (err) return err;

	vsfdbg_printf("httpd: started on port %d" VSFCFG_DEBUG_LINEEND, APPCFG_HTTPD_PORT);
	vsfip_httpd_start(httpd, APPCFG_HTTPD_PORT);

	vsfsm_pt_end(pt);
	return VSFERR_NONE;
}

static void usrapp_usbd_conn(void *p)
{
	struct usrapp_t *app = (struct usrapp_t *)p;

	vsfusbd_device_init(&app->usbd.device);
	vsfusbd_connect(&app->usbd.device);
	if (app_hwcfg.usbd.pullup.port != VSFHAL_DUMMY_PORT)
		vsfhal_gpio_set(app_hwcfg.usbd.pullup.port, 1 << app_hwcfg.usbd.pullup.pin);
}

void usrapp_srt_init(struct usrapp_t *app)
{
	vsf_usart_stream_init(&app->debug.uart_stream);
	vsfdbg_init((struct vsf_stream_t *)&usrapp.debug.stream_tx);

	if (app_hwcfg.usbd.pullup.port != VSFHAL_DUMMY_PORT)
	{
		vsfhal_gpio_init(app_hwcfg.usbd.pullup.port);
		vsfhal_gpio_clear(app_hwcfg.usbd.pullup.port, 1 << app_hwcfg.usbd.pullup.pin);
		vsfhal_gpio_config(app_hwcfg.usbd.pullup.port, app_hwcfg.usbd.pullup.pin, VSFHAL_GPIO_OUTPP);
	}
	vsfusbd_disconnect(&app->usbd.device);
	vsftimer_create_cb(200, 1, usrapp_usbd_conn, app);

	vsfusbh_init(&usrapp.usbh.usbh);
	vsfusbh_register_driver(&usrapp.usbh.usbh, &vsfusbh_hub_drv);
	vsfusbh_ecm_cb.param = &usrapp.net.usbh_ecm;
	vsfusbh_ecm_cb.on_connect = usrapp_net_on_connect;
	vsfusbh_ecm_cb.on_connected = usrapp_net_on_connected;
	vsfusbh_ecm_cb.on_disconnect = usrapp_net_on_disconnect;
	vsfusbh_ecm_cb.on_data = usrapp_net_on_data;
	vsfusbh_register_driver(&usrapp.usbh.usbh, &vsfusbh_ecm_drv);
	vsfusbh_ipheth_cb.param = &usrapp.net.usbh_ipheth;
	vsfusbh_ipheth_cb.on_connect = usrapp_net_on_connect;
	vsfusbh_ipheth_cb.on_connected = usrapp_net_on_connected;
	vsfusbh_ipheth_cb.on_disconnect = usrapp_net_on_disconnect;
	vsfusbh_ipheth_cb.on_data = usrapp_net_on_data;
	vsfusbh_register_driver(&usrapp.usbh.usbh, &vsfusbh_ipheth_drv);

	vsfsm_pt_init(&app->tcpip.sm, &app->tcpip.pt);
}

#include "component/port/linux/vsflinux.h"
int linux_main(int argc, char *argv[])
{
	return 0;
}

void usrapp_nrt_init(struct usrapp_t *app)
{
	vsflinux_init();
	vsflinux_execl(linux_main, "main", NULL);
}
